package org.robotlegs.xxl.base 
{
	import flash.utils.describeType;
	import flash.utils.Dictionary;
	import org.osflash.signals.ISignal;
	import org.osflash.signals.Signal;
	import org.robotlegs.base.ContextError;
	import org.robotlegs.core.IInjector;
	import org.robotlegs.core.IReflector;
	import org.robotlegs.xxl.core.ISignalCommandMap;
	import org.robotlegs.xxl.core.ISignalMap;
	import org.robotlegs.xxl.mvcs.Command;
	
	/**
	 * 用Signal来替换原来的CommandMap中的事件机制
	 * @author XXL
	 */
	public class SignalCommandMap implements ISignalCommandMap 
	{
		protected var signalMap:ISignalMap;
		
		protected var injector:IInjector;
		protected var reflector:IReflector;
		protected var signalCommandMap:Dictionary;
		protected var signalClassMap:Dictionary;
		protected var verifiedCommandClasses:Dictionary;
		protected var checkedSignals:Dictionary;
		
		public function SignalCommandMap(injector:IInjector, reflector:IReflector, signalMap:ISignalMap) 
		{
			this.injector = injector;
			this.reflector = reflector;
			this.signalMap = signalMap;
			signalCommandMap = new Dictionary();
			signalClassMap = new Dictionary();
			verifiedCommandClasses = new Dictionary();
			checkedSignals = new Dictionary();
		}
		
		protected function verifiedCommandClass(commandClass:Class):void 
		{
			if (verifiedCommandClasses[commandClass])
			{
				return;
			}
			if (describeType(commandClass).factory.method.(@name = "execute").length() != 1)
			{
				throw new ContextError(ContextError.E_COMMANDMAP_NOIMPL + " - " + commandClass);
			}
			verifiedCommandClasses[commandClass] = true;
		}
		
		protected function checkSignalValueConflict(signal:ISignal):void
		{
			var className:String = reflector.getFQCN(signal);
			if (checkedSignals[className])
			{
				return;
			}
			var checkDic:Dictionary = new Dictionary();
			var len:int = signal.valueClasses.length;
			for (var i:int = 0; i < len; i++) 
			{
				if (checkDic[signal.valueClasses[i]] == 0)
				{
					checkedSignals[className] = true;
					return;
				}
				else
				{
					checkDic[signal.valueClasses[i]] = 0;
				}
			}
			checkedSignals[className] = false;
		}
		
		protected function routeSignalToCommand(signal:ISignal, valueObjects:Array, commandClass:Class, oneshot:Boolean):void
		{
			var signalClassName:String = reflector.getFQCN(signal);
			var className:String = reflector.getFQCN(commandClass);
			var useName:Boolean = checkedSignals[signalClassName];
			var i:int;
			var len:int = signal.valueClasses.length;
			var tmpName:String = "";
			for (i = 0; i < len; i++) 
			{
				if (useName)
				{
					tmpName = className + "_" + i;
				}
				injector.mapValue(signal.valueClasses[i], valueObjects[i], tmpName);
			}
			var cmd:Command = (injector.instantiate(commandClass));
			for (i = 0; i < len; i++)
			{
				if (useName)
				{
					tmpName = className + "_" + i;
				}
				injector.unmap(signal.valueClasses[i], tmpName);
			}
			cmd.execute();
			if (oneshot)
			{
				unmapSignal(signal, commandClass);
			}
		}
		
		private function unmapSignal(signal:ISignal, commandClass:Class):void 
		{
			var callbacksByCommandClass:Dictionary = signalCommandMap[signal];
			if (callbacksByCommandClass == null)
			{
				return;
			}
			var callback:Function = callbacksByCommandClass[commandClass];
			if (callback == null)
			{
				return;
			}
			signalMap.unmapSignal(signal, callback);
			delete callbacksByCommandClass[commandClass];
		}
		
		/* INTERFACE xxl.core.ISignalCommandMap */
		
		public function hasSignalCommand(signalClass:Class, commandClass:Class):Boolean 
		{
			var signal:ISignal = injector.getInstance(signalClass);
			if (signal == null)
			{
				return false;
			}
			var callbacksByCommandClass:Dictionary = signalCommandMap[signal];
			if (callbacksByCommandClass == null)
			{
				return false;
			}
			var callback:Function = callbacksByCommandClass[commandClass];
			return callback != null;
		}
		
		public function mapSignalClass(signalClass:Class, commandClass:Class, oneShot:Boolean = false):ISignal 
		{
			verifiedCommandClass(commandClass);
			var signal:ISignal = signalMap.registerSignal(signalClass);
			if (hasSignalCommand(signalClass, commandClass))
			{
				return signal;
			}
			checkSignalValueConflict(signal);
			var callbacksByCommandClass:Dictionary = signalCommandMap[signal] ||= new Dictionary();
			var callback:Function = function(...rest):void
			{
				routeSignalToCommand(signal, rest, commandClass, oneShot);
			};
			callbacksByCommandClass[commandClass] = callback;
			signalMap.mapSignal(signal, callback);
			return signal;
		}
		
		public function unmapSignalClass(signalClass:Class, commandClass:Class):void 
		{
			var signal:ISignal = injector.getInstance(signalClass);
			if (signal == null)
			{
				return;
			}
			unmapSignal(signal, commandClass);
		}
		
		public function unmapSignals():void 
		{
			signalMap.unmapSignals();
			signalCommandMap = new Dictionary();
			signalClassMap = new Dictionary();
			verifiedCommandClasses = new Dictionary();
		}
		
	}

}